﻿using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using VideoReflector.Models;
using nVLC.Declarations.Players;
using nVLC.Declarations.Media;
using nVLC.Declarations.Filters;
using nVLC.Declarations;
using nVLC.Implementation;

namespace VideoReflector.Video
{
    /// <summary>
    /// Implementation of IClipPlayer which uses libvlc to allow playback of session video clips on screen
    /// </summary>
    public class VlcClipPlayer : VlcClipStreamer, IClipPlayer
    {
        #region Constructor

        /// <summary>
        /// Constructor for VlcClipPlayer object
        /// </summary>
        /// <param name="clipTags"> Collection of ClipTag objects which correspond to the clips to be exported </param>
        /// <param name="sessionVideo"> Object containing details of video file associated with session </param>
        /// <param name="sessionFileDirectory"> Directory where session files are contained in on disk </param>
        /// <param name="displayContainer"> WPF Panel object to use as container for video display output </param>
        public VlcClipPlayer(IList<ClipTag> clipTags, VideoFileType sessionVideo, string sessionFileDirectory, Panel displayContainer)
            : base(clipTags, sessionVideo, sessionFileDirectory)
        {
            base.Initialise();
            // send notification event on player position update to allow for binding
            _vlcListPlayer.Events.PlayerPositionChanged += (s, e) =>
            {
                NotifyPropertyChanged("CurrentClipPosition");
                NotifyPropertyChanged("VideoPosition");
                NotifyPropertyChanged("VideoTime");
            };
            // set up display output
            VlcDisplayController.SetUpDisplay(_vlcListPlayer.InnerPlayer, displayContainer, 
                sessionVideo.Resolution.Height, sessionVideo.Resolution.Width);
        }

        #endregion // Constructor

        #region IClipPlayer implementations

        public void Pause()
        {
            _vlcListPlayer.Pause();
        }

        public void SkipToNext()
        {
            _vlcListPlayer.PlayNext();
        }

        public void SkipToPrevious()
        {
            _vlcListPlayer.PlayPrevious();
        }

        public double CurrentClipPosition
        {
            // VLC player position reflects position within whole video file, therefore need to scale and offset
            // for each clip
            get 
            {
                if (ClipNumber != -1 && _clipTags.Count != 0)
                {
                    // HACK: multiplied by fudge-factor to compensate for bug in VLC
                    double playerPos = _vlcListPlayer.Position * 30 / _sessionVideo.FrameRate;
                    double clipStartPosition = (double)_clipTags[ClipNumber].StartTime / _sessionVideo.Length;
                    double clipEndPosition = (double)_clipTags[ClipNumber].EndTime / _sessionVideo.Length;
                    double clipPosition = (playerPos - clipStartPosition) / (clipEndPosition - clipStartPosition);
                    // ensure rounding errors haven't pulled outside valid range
                    double boundedPosition = clipPosition >= 0.0 ? (clipPosition <= 1.0 ? clipPosition : 1.0) : 0.0;
                    return boundedPosition;
                }
                else
                    return 0;
            }
            set
            {
                if (ClipNumber != -1 && _clipTags.Count != 0)
                {
                    double clipStartPosition = (double)_clipTags[ClipNumber].StartTime / _sessionVideo.Length;
                    double clipEndPosition = (double)_clipTags[ClipNumber].EndTime / _sessionVideo.Length;
                    // HACK: mutlitplied by fudge-factor to compensate for bug in VLC
                    double playerPosition = ((value * (clipEndPosition - clipStartPosition)) + clipStartPosition)
                                                    * _sessionVideo.FrameRate / 30;
                    // ensure rounding errors haven't pulled outside valid range
                    double boundedPosition = playerPosition >= 0.0 ? (playerPosition <= 1.0 ? playerPosition : 1.0) : 0.0;
                    _vlcListPlayer.Position = (float)boundedPosition;
                    NotifyPropertyChanged("CurrentClipPosition");
                }
            }
        }

        public new int ClipNumber
        {
            get 
            {
                return base.ClipNumber;
            }
            set
            {
                _currentClipIndex = value;
                _vlcListPlayer.PlayItemAt(_currentClipIndex);
                NotifyPropertyChanged("ClipNumber");
            }
        }

        public int Volume
        {
            get
            {
                return _vlcListPlayer.InnerPlayer.Volume;
            }
            set
            {
                int boundedValue = value >= 0 ? (value <= 100 ? value : 100) : 0;
                _vlcListPlayer.InnerPlayer.Volume = boundedValue;
                NotifyPropertyChanged("Volume");
            }
        }

        public double VideoPosition
        {
            get
            {
                // HACK: mutlitplied by fudge-factor to compensate for bug in VLC
                var pos = _vlcListPlayer.Position * 30 / _sessionVideo.FrameRate;
                pos = pos < 0 ? 0 : pos > 1 ? 1 : pos;
                return pos;
            }
            set
            {
                var pos = (float)value * _sessionVideo.FrameRate / 30;
                pos = pos < 0 ? 0 : pos > 1 ? 1 : pos;
                _vlcListPlayer.Position = pos;
                NotifyPropertyChanged("VideoPosition");
            }
        }

        public int VideoTime
        {
            get
            {
                return (int) Math.Round(VideoPosition * _sessionVideo.Length);
            }
            set
            {
                VideoPosition = ((double)value) / _sessionVideo.Length;
                NotifyPropertyChanged("VideoTime");
            }
        }

        #endregion // IClipPlayer implementations
    }
}
